package com.fingard.dsp.bank.directbank.fngrd01;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fingard.common.utils.json.JSONUtils;
import com.fingard.constant.Format;
import com.fingard.crypto.DigestHelper;
import com.fingard.diagnostics.LogHelper;
import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.allinpay02.util.HttpConnectionUtil;
import com.fingard.dsp.bank.directbank.fngrd01.util.Base64Helper;
import com.fingard.dsp.bank.directbank.fngrd01.util.RSAHelper;
import com.fingard.dsp.bank.directbank.fngrd01.util.SignAPI;
import com.fingard.dsp.bank.directbank.fngrd01.util.TripleDESHelper;
import com.fingard.net.ITcpClient;
import com.fingard.net.TcpPortClient;
import com.fingard.net.TcpReceiver;
import com.fingard.net.WebRequest;
import com.fingard.text.StringHelper;
import com.fingard.util.DateHelper;
import com.fingard.util.SortHelper;
import com.fingard.util.TransIDHelper;
import com.fingard.xml.XmlTextWriter;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.text.ParseException;
import java.util.*;

public class FnGrd01Base extends DirectBase implements ITcpClient {
    private static TransIDHelper idHelper = new TransIDHelper(1, 999);

    protected String getQryTransID() {
        Date tmpDateNow = new Date();
        return Format.DateTime17Format.format(tmpDateNow) + idHelper.getNextIDStr();
    }

    /**
     * 获取字符集编码
     *
     * @return
     */
    protected String getCharset() {
        return getBankConfig().getCharset("GBK");
    }

    /**
     * 生成随机字符串
     *
     * @return
     * @throws Exception
     */
    protected String get_nonce_str() throws Exception {
        String time_stamp = DateHelper.getSecsFrom1970();
        // 随机字符串 String(32)
        Random random = new Random();
        long randLong = random.nextLong();
        randLong = random.nextLong();
        String nonce_str = time_stamp + String.format("%20d", randLong);
        nonce_str = DigestHelper.cryptoMD5RetHexStr(nonce_str.getBytes());
        return nonce_str;
    }

    /**
     * 生成待签名数据 待签名数据格式： a_b_c_d_***_saltKey
     *
     * @param mapParams
     * @return
     */
    protected String getSignStr(Map<String, String> mapParams, String saltKey) {
        Map<String, String> signMap = new HashMap<String, String>();

        for (String key : mapParams.keySet()) {
            String value = mapParams.get(key);

            if (StringHelper.hasAnyChar(value) && !"Signature".equals(key) && !"sign".equals(key)) {
                signMap.put(key, value);
            }
        }

        // Map排序
        signMap = SortHelper.sortMapByName(signMap);

        // Map转HTTP字符串
        String signStr = "";
        for (String keys : signMap.keySet()) {
            if (signStr.length() > 0) {
                signStr += "_";
            }
            signStr += signMap.get(keys);
        }
        signStr += "_" + saltKey;

        WriteBankLogLn2("签名原始数据:" + signStr);

        return signStr;
    }

    /**
     * map转json
     *
     * @param mapParams map
     * @return json
     * @throws UnsupportedEncodingException
     */
    protected String getReqPostJson(Map<String, String> mapParams) throws UnsupportedEncodingException {
        JSONObject reqPostJson = new JSONObject();

        for (String key : mapParams.keySet()) {
            reqPostJson.put(key, mapParams.get(key));
        }

        WriteBankLogLn2("请求原始json数据:\r\n" + reqPostJson.toJSONString());

        return reqPostJson.toJSONString();
    }

    protected String getPostJson(Map<String, Object> mapParams) throws UnsupportedEncodingException {
        JSONObject reqPostJson = new JSONObject();

        for (String key : mapParams.keySet()) {
            reqPostJson.put(key, mapParams.get(key));
        }

        WriteBankLogLn2("请求原始json数据:\r\n" + reqPostJson.toJSONString());

        return reqPostJson.toJSONString();
    }

    public static String oriSignStr(Map<String, Object> data, String key) {
        // 生成原始签名字符串
        StringBuilder sb = new StringBuilder();
        genOriSignStr(data, sb);
        return sb.toString() + "_" + key;
    }

    @SuppressWarnings("unchecked")
    private static void genOriSignStr(Map<String, Object> data, StringBuilder sb) {
        Set<String> keySet = data.keySet();
        List<String> keyList = new ArrayList<String>(keySet.size());
        keyList.addAll(keySet);

        // 将所有请求字段名称按 ASCII 码从小到大排序
        Collections.sort(keyList);

        for (String s : keyList) {
            Object obj = data.get(s);

            if (obj == null) {
                continue;
            }

            // 暂时只能处理 String 类型的值
            if (obj instanceof String) {
                if (!"".equals(obj)) {
                    sb.append(obj + "_");
                }
            } else if (obj instanceof Map) {
                genOriSignStr((Map<String, Object>) obj, sb);
            } else {
                sb.append(String.valueOf(obj) + "_");
            }
        }
    }

    /**
     * 扫码付发送融汇
     *
     * @param p_reqStr
     * @param url
     * @return
     * @throws Exception
     */
    protected String[] sendToFnGrd(String p_reqStr, String url) throws Exception {
        String[] retStr = new String[]{"", ""};

        WebRequest tmpRequest = new WebRequest(url);
        tmpRequest.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

        WriteBankLogLn2("提交融汇通服务地址:" + url);

        byte[] tmpWriteBytes = p_reqStr.getBytes("UTF-8");
        retStr = tmpRequest.upload(tmpWriteBytes, "UTF-8");

        WriteBankLogLn2("融汇原始响应数据:\r\n" + retStr[1]);

        return retStr;
    }

    /**
     * 获取有效时间 (分钟)
     *
     * @return 有效时间
     * @throws ParseException
     *             日期格式转换异常
     */
    public String getValidTime(String validTime) throws ParseException {
        if (Long.parseLong(validTime) >= 15*24*60) {
            validTime = "10d";
        } else {
            validTime += "m";
        }
        return validTime;
    }


    /**
     * 解密融汇返回密文
     *
     * @param respFnGrd       融汇返回密文
     * @return 融汇响应明文
     */
    protected String parseFnGrdRespForUnURLDecoder(TripleDESHelper tripleDESHelper, String respFnGrd) throws Exception {
        String respPlaintext = "";
        try {
            respPlaintext = tripleDESHelper.decrypt(respFnGrd);
            respPlaintext = new String(new BASE64Decoder().decodeBuffer(respPlaintext), "GBK");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return respPlaintext;
    }

    protected String decryptByTripleDES(String p_reqStr) {
        TripleDESHelper tripleDESHelper = new TripleDESHelper();
        tripleDESHelper.initCBC(getBankConfig().getValueAsString("keyBase64"), getBankConfig().getValueAsString("ivBase64"));

        String respPlaintext = "";
        byte[] respBase64Str;
        try {
            respBase64Str = Base64Helper.decode(URLDecoder.decode(p_reqStr, getCharset()));
            respPlaintext = new String(tripleDESHelper.decrypt(respBase64Str), getCharset());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return respPlaintext;
    }

    protected String parseFnGrdResp(TripleDESHelper tripleDESHelper, String respFnGrd) {
        String respPlaintext = "";
        byte[] respBase64Str;
        try {
            respBase64Str = Base64Helper.decode(URLDecoder.decode(respFnGrd, "UTF-8"));
            respPlaintext = new String(tripleDESHelper.decrypt(respBase64Str), getCharset());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return respPlaintext;
    }

    /**
     * 通知验签 解析融汇响应获取签名及待签名串
     *
     * @param p_reqStr    融汇响应原始数据
     * @param commandCode 指令代码
     * @param saltKey     API密钥
     * @return respStr[0]: 待签名数据 respStr[1]: 签名
     */
    protected String[] getFnGrdSign(String p_reqStr, String commandCode, String saltKey) {
        String[] respStr = new String[]{"", ""};

        Map<String, String> signMap = new HashMap<String, String>();

        JSONObject respJSON = JSON.parseObject(p_reqStr);

        String status = respJSON.getString("Status");
        String returnMsg = respJSON.getString("ReturnMsg");
        String enterpriseNum = respJSON.getString("EnterpriseNum");
        String mchId = respJSON.getString("MchId");
        String nonceStr = respJSON.getString("NonceStr");
        String openId = respJSON.getString("OpenId");
        String outTradeNo = respJSON.getString("OutTradeNo");
        String fgTradeNo = respJSON.getString("FGTradeNo");
        String totalFee = respJSON.getString("TotalFee");
        String feeType = respJSON.getString("FeeType");
        String attach = respJSON.getString("Attach");
        String transactionID = respJSON.getString("Transaction_id");
        String timeEnd = respJSON.getString("TimeEnd");
        String callResult = respJSON.getString("CallResult");
        String signature = respJSON.getString("Signature");
        String userId = respJSON.getString("UserId");
        String payType = respJSON.getString("PayType");
        String tradeState = respJSON.getString("TradeState");

        signMap.put("Status", status);
        signMap.put("ReturnMsg", returnMsg);
        signMap.put("EnterpriseNum", enterpriseNum);
        signMap.put("MchId", mchId);
        signMap.put("NonceStr", nonceStr);
        if ("UNQRPAY01".equals(commandCode)) {
            signMap.put("UserId", userId);
            signMap.put("PayType", payType);
            signMap.put("TradeState", tradeState);
        } else {
            signMap.put("OpenId", openId);
        }

        signMap.put("OutTradeNo", outTradeNo);
        signMap.put("FGTradeNo", fgTradeNo);
        signMap.put("TotalFee", totalFee);
        signMap.put("FeeType", feeType);
        signMap.put("Attach", attach);
        signMap.put("Transaction_id", transactionID);
        signMap.put("TimeEnd", timeEnd);
        signMap.put("CallResult", callResult);
        signMap.put("Signature", signature);

        /**
         * 支付宝及微信扫码以commandCode区分
         *
         * FGNATIVE01 融汇通扫码支付（微信） FGNATIVE02 融汇通扫码支付（支付宝）
         */
        if ("FGNATIVE02".equals(commandCode)) {

        } else if ("FGNATIVE01".equals(commandCode) || "JSAPI01".equals(commandCode)) {
            String isSubscribe = respJSON.getString("IsSubscribe");
            String bankType = respJSON.getString("BankType");
            String bankBillNo = respJSON.getString("BankBillNo");

            signMap.put("IsSubscribe", isSubscribe);
            signMap.put("BankType", bankType);
            signMap.put("BankBillNo", bankBillNo);
        }

        String plainStr = getSignStr(signMap, saltKey);

        respStr[0] = plainStr;
        respStr[1] = signature;

        return respStr;
    }

    protected String[] getNewFnGrdSign(String p_reqStr, String saltKey) {
        String[] respStr = new String[]{"", ""};

        Map<String, String> signMap = JSONUtils.json2Map(p_reqStr);

        JSONObject respJSON = JSON.parseObject(p_reqStr);
        String signature = respJSON.getString("Signature");

        String plainStr = getSignStr(signMap, saltKey);

        respStr[0] = plainStr;
        respStr[1] = signature;

        return respStr;
    }

    /**
     * 签名
     *
     * @param actItem      商户配置信息
     * @param p_reqSignStr 待签名串
     * @return 签名结果
     * @throws Exception
     * @throws SignatureException
     * @throws InvalidKeyException
     */
    protected String signFnGrd(ActSetItem actItem, String p_reqSignStr) throws InvalidKeyException, SignatureException, Exception {
        RSAHelper rsaHelper = null;
        String signature = "";

        // try {
        rsaHelper = new RSAHelper();
        rsaHelper.setPrivateKeyByXml(actItem.ownKeyStorePath);
        signature = new SignAPI(rsaHelper).signToBase64(p_reqSignStr);
        // } catch (NoSuchAlgorithmException e) {
        // e.printStackTrace();
        // } catch (InvalidKeySpecException e) {
        // e.printStackTrace();
        // } catch (IOException e) {
        // e.printStackTrace();
        // } catch (InvalidKeyException e) {
        // e.printStackTrace();
        // } catch (SignatureException e) {
        // e.printStackTrace();
        // } catch (Exception e) {
        // e.printStackTrace();
        // }

        return signature;
    }

    /**
     * 融汇响应验签
     *
     * @param plainStr 待签名数据
     * @param signStr  融汇签名
     * @param actItem  商户证书配置
     * @return 验签结果
     */
    protected boolean verifyFnGrd(String plainStr, String signStr, ActSetItem actItem) {
        boolean flag = false;

        RSAHelper pubHelper = new RSAHelper();
        try {
            pubHelper.setPublicKeyByXml(actItem.oppCertFilePath);

            // 创建 API 实例
            SignAPI verifysign = new SignAPI(pubHelper);

            // 用验签码验签
            flag = verifysign.verifySign(plainStr, signStr);

            // 判断验签是否通过
            if (flag) {
                return true;
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * 加密/编码请求json数据
     *
     * @param p_reqJson 请求Json
     * @return 密文json
     */
    protected String getReqSendData(String enterpriseNum, String p_reqJson) {
        return getReqSendData(enterpriseNum, p_reqJson, "UTF-8");
    }

    protected String getReqSendData(String enterpriseNum, String p_reqJson, String character) {
        String data = "";
        String base64Str = "";
        WriteBankLogLn2("从DSP发出的明文:\r\n" + p_reqJson);
        try {
            boolean encryptFlag = Boolean.parseBoolean(getBankConfig().getValueAsString("isOpenEncrypt"));
            if (encryptFlag == false) {
                // 编码
                base64Str = Base64Helper.encode(p_reqJson.getBytes(character));
                data = URLEncoder.encode(base64Str, character);
                data = URLEncoder.encode(data, character);
            } else {
                TripleDESHelper tripleDESHelper = new TripleDESHelper();
                tripleDESHelper.initCBC(getBankConfig().getValueAsString("keyBase64"), getBankConfig().getValueAsString("ivBase64"));
                // 加密
                base64Str = new BASE64Encoder().encode(p_reqJson.toString().getBytes(character));
                data = URLEncoder.encode(tripleDESHelper.encrypt(base64Str), character);
            }
            if ("UTF-8".equals(character)) {
                data = "EnterpriseNum=" + enterpriseNum + "&Data=" + data;
                WriteBankLogLn2("融汇通POST请求数据:\r\n" + data);
            } else {
                data = "ReqXml=" + data;
                WriteBankLogLn2("POST请求数据:\r\n" + data);
            }
        } catch (Exception e) {
            WriteBankLogLn2("数据加密/编码出错，原因如下:\r\n" + LogHelper.getStackTrace(e));
        }

        return data;
    }

    /**
     * 解密/解码融汇通原始响应数据
     *
     * @param p_respData 原始响应数据
     * @return 明文响应数据
     */
    protected String getRespPostData(String p_respData) {
        return getRespPostData(p_respData, "UTF-8", "");
    }

    protected String getRespPostData(String p_respData, String character, String respType) {
        String respData = "";

        try {
            boolean encryptFlag = Boolean.parseBoolean(getBankConfig().getValueAsString("isOpenEncrypt"));
            if (encryptFlag == false) {
                // 解码
                byte[] respBase64Str = Base64Helper.decode(URLDecoder.decode(p_respData, character));
                respData = new String(respBase64Str, character);
            } else {
                TripleDESHelper tripleDESHelper = new TripleDESHelper();
                tripleDESHelper.initCBC(getBankConfig().getValueAsString("keyBase64"), getBankConfig().getValueAsString("ivBase64"));

                // 解密
                if ("QueryPayState".equals(respType) || "MicroPayFnGrd".equals(respType)) {
                    respData = parseFnGrdRespForUnURLDecoder(tripleDESHelper, p_respData);
                } else {
                    respData = parseFnGrdResp(tripleDESHelper, p_respData);
                }
            }
            WriteBankLogLn2("融汇通返回POST明文数据:" + respData);
        } catch (Exception e) {
            WriteBankLogLn2("数据解密/解码出错，原因如下:\r\n" + LogHelper.getStackTrace(e));
        }

        return respData;
    }

    protected void writeCommonHeader(XmlTextWriter xmlWriter, FnGrd01Header bankHeader) throws Exception {
        if (StringHelper.isNullOrEmpty(bankHeader.CommandCode)) {
            throw new Exception("未赋值CommandCode");
        }
        if (StringHelper.isNullOrEmpty(bankHeader.CorpBankCode)) {
            throw new Exception("未赋值CorpBankCode");
        }
        if (StringHelper.isNullOrEmpty(bankHeader.TransSeqID)) {
            throw new Exception("未赋值TransSeqID");
        }
        xmlWriter.writeStartElement("Root");
        xmlWriter.writeStartElement("Head");
        xmlWriter.writeElementString("EnterpriseNum", bankHeader.EnterpriseNum);
        xmlWriter.writeElementString("CommandCode", bankHeader.CommandCode);
        xmlWriter.writeElementString("CorpBankCode", bankHeader.CorpBankCode);
        xmlWriter.writeElementString("TransSeqID", bankHeader.TransSeqID);
        xmlWriter.writeElementClosed("VerifyCode");
        xmlWriter.writeElementClosed("ZipType");
        xmlWriter.writeElementString("SourceChannel", bankHeader.SourceChannel);
        xmlWriter.writeEndElement();
    }

    protected void writeRealTimeCommonHeader(XmlTextWriter xmlWriter, FnGrd01Header bankHeader) throws Exception {
        if (StringHelper.isNullOrEmpty(bankHeader.CommandCode)) {
            throw new Exception("未赋值CommandCode");
        }
        xmlWriter.writeStartElement("Root");
        xmlWriter.writeStartElement("Head");
        xmlWriter.writeElementClosed("EnterpriseNum");
        xmlWriter.writeElementString("CorpBankCode", bankHeader.CorpBankCode);
        xmlWriter.writeElementString("CommandCode", bankHeader.CommandCode);
        xmlWriter.writeElementString("TransSeqID", bankHeader.TransSeqID);
        xmlWriter.writeElementClosed("VerifyCode");
        xmlWriter.writeElementClosed("ZipType");
        xmlWriter.writeEndElement();
    }

    protected int defaultTimeoutSec = 30;

    protected String[] sendToBank(String p_reqStr) throws Exception {

        String[] retStr = new String[]{"", ""};

        WriteBankLogLn2("2.提交银行的地址：" + bankFront.serverIP + ":" + bankFront.serverPort);

        TcpPortClient tmpClient = new TcpPortClient(bankFront.serverIP, Integer.parseInt(bankFront.serverPort), this);
        tmpClient.timeoutMil = this.getReadTimeout(defaultTimeoutSec);
        String tmpCharset = getCharset();
        byte[] tmpBytesBody = p_reqStr.getBytes(tmpCharset);
        String tmpStrLen = String.format("% 8d", tmpBytesBody.length);
        String tmpHead = "Content-Length:" + tmpStrLen + "\r\n";
        byte[] tmpBytesHead = tmpHead.getBytes(tmpCharset);

        byte[] tmpBytesSend = new byte[tmpBytesHead.length + tmpBytesBody.length];
        System.arraycopy(tmpBytesHead, 0, tmpBytesSend, 0, tmpBytesHead.length);
        System.arraycopy(tmpBytesBody, 0, tmpBytesSend, tmpBytesHead.length, tmpBytesBody.length);

        WriteBankLogStep2(tmpHead + p_reqStr);
        TcpReceiver tmpReceiver = tmpClient.sendMessage(tmpBytesSend);
        if (StringHelper.isNullOrEmpty(tmpReceiver.connState)) {
            String tmpBkStr = new String(tmpReceiver.recBytes, 0, tmpReceiver.readedLength, tmpCharset);
            WriteBankLogStep3(tmpBkStr);
        }
        retStr[0] = tmpReceiver.connState;
        if (tmpReceiver.isCompleted) {
            retStr[1] = new String(tmpReceiver.recBytes, tmpReceiver.contentStartIndex, tmpReceiver.contentLength, tmpCharset);
        } else {
            retStr[1] = tmpReceiver.getConnMsg();
            WriteBankLogLn(retStr[1]);
        }
        return retStr;
    }

    public void getContentLength(TcpReceiver p_receiver) throws Exception {
        byte tmpByteLt = "<".getBytes(getCharset())[0];
        if (p_receiver.contentLength == -1) {
            for (int i = 0; i < p_receiver.readedLength; i++) {
                if (p_receiver.recBytes[i] == tmpByteLt) {
                    if (p_receiver.contentStartIndex <= 0) {
                        p_receiver.contentStartIndex = i;
                    }
                    String tmpHeadStr = new String(p_receiver.recBytes, 0, i, getCharset());
                    String[] tmpKeyValues = tmpHeadStr.split("\n");
                    for (int j = 0; j < tmpKeyValues.length; j++) {
                        String[] tmpEach = tmpKeyValues[j].split(":");
                        if (tmpEach[0].toLowerCase().equals("content-length")) {
                            p_receiver.contentLength = Integer.valueOf(tmpEach[1].trim());
                        }
                    }
                    break;
                }
            }
        }
    }

    protected HashMap<String, String> getOpenid(String p_appId, String p_appSecret, String p_code) {
        HashMap<String, String> retHash = new HashMap<String, String>();
        String respCode = "";
        String respInfo = "";
        String tmpUrl = bankFront.getUrl("access_token");
        if (StringHelper.isNullOrEmpty(tmpUrl)) {
            respCode = "-1";
            respInfo = "未设置“通过code换取网页授权access_token”的地址";
        } else {
            tmpUrl = tmpUrl + "?appid=" + p_appId + "&secret=" + p_appSecret + "&code=" + p_code + "&grant_type=authorization_code";
            this.WriteBankLogStep2(tmpUrl);
            WebRequest tmpWebRequest = new WebRequest(tmpUrl);
            String[] tmpRet = tmpWebRequest.upload(null, this.getCharset());
            if (tmpRet[0].length() == 0) {
                this.WriteBankLogStep3(tmpRet[1]);
                if (tmpRet[1].startsWith("{")) {
                    retHash = JSONUtils.json2Map(tmpRet[1]);
                    respCode = "0";
                } else {
                    respCode = "-1";
                    respInfo = tmpRet[1];
                }
            } else {
                this.WriteBankLogLn(tmpRet[0] + "\r\n" + tmpRet[1]);
                respCode = "-1";
                respInfo = tmpRet[1];
            }
        }
        retHash.put("RespCode", respCode);
        retHash.put("RespInfo", respInfo);
        return retHash;
    }

    /**
     * 统一下单数据提交
     *
     * @param reqPostData
     * @param url         请求地址
     * @return
     * @throws Exception
     */
    protected String sendUnifiedToBank(String reqPostData, String url) throws Exception {
        WriteBankLogLn2("融汇统一下单服务地址:" + url);

        WriteBankLogLn2("融汇统一下单请求数据:\r\n" + reqPostData);

        HttpConnectionUtil http = new HttpConnectionUtil(url);
        http.init();
        byte[] bys = http.postParams(reqPostData, true);

        String result = new String(bys, "UTF-8");
        WriteBankLogLn2("融汇统一下单响应数据:\r\n" + result);

        return result;
    }

    /**
     * by leo
     *
     * @param data
     * @param secretKey
     * @return
     */
    public static String getWebapiToSignData(Map<String, String> data, String secretKey) {
        StringBuilder sb = new StringBuilder();
        Set<String> keySet = data.keySet();
        List<String> keyList = new ArrayList<String>();
        keyList.addAll(keySet);
        Collections.sort(keyList);
        for (String s : keyList) {
            // Signature字段不参与运算
            if ("Signature".equals(s)) {
                continue;
            }
            String val = data.get(s);
            // 空值不参与运算
            if (val != null && !"".equals(val)) {
                sb.append(val).append("_");
            }
        }
        sb.append(secretKey);
        return sb.toString();
    }

    /**
     * 获取webapi签名字符串
     *
     * @param actItem
     * @param p_reqSignStr
     * @return
     * @throws InvalidKeyException
     * @throws SignatureException
     * @throws Exception
     */
    protected String signWebAPIFnGrd(ActSetItem actItem, String p_reqSignStr) throws InvalidKeyException, SignatureException, Exception {
        RSAHelper rsaHelper = null;
        String signature = "";

        // try {
        rsaHelper = new RSAHelper();
        rsaHelper.setPrivateKeyByXml(actItem.ownKeyWebAPIStorePath);
        signature = new SignAPI(rsaHelper).signToBase64(p_reqSignStr);

        return signature;
    }

    protected boolean verifySign(ActSetItem actItem, String p_respToSignStr, String signature) throws InvalidKeyException, SignatureException, Exception {
        boolean flag = false;

        RSAHelper pubHelper = new RSAHelper();
        try {
            pubHelper.setPublicKeyByXml(actItem.oppCertWebAPIFilePath);

            // 创建 API 实例
            SignAPI verifysign = new SignAPI(pubHelper);

            // 用验签码验签
            flag = verifysign.verifySign(p_respToSignStr, signature);

            // 判断验签是否通过
            if (flag) {
                return true;
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;

    }

    /**
     * 获取webapi 加密内容
     *
     * @param is3des
     * @param sourceJson
     * @return
     * @throws Exception
     */
    protected String getDataContent(boolean is3des, String enterpriseNum, String sourceJson) throws Exception {
        String str = "";
        // 3des加密
        if (is3des) {
            byte[] bytes = sourceJson.getBytes("GBK");
            TripleDESHelper tripleDESHelper = new TripleDESHelper();
            tripleDESHelper.initCBC(getBankConfig().getValueAsString("keyBase64"), getBankConfig().getValueAsString("ivBase64"));
            bytes = tripleDESHelper.encrypt(bytes);
            str = new BASE64Encoder().encode(bytes);
        } else {
            byte[] bytes = sourceJson.getBytes("UTF-8");
            str = Base64Helper.encode(bytes);
            str = URLEncoder.encode(str, "UTF-8");
            str = URLEncoder.encode(str, "UTF-8");// 因为以流的方式发送，已进入融汇自动URL解码，所以要再加一层URL编码，融汇解码才正常。
        }
        str = "EnterpriseNum=" + enterpriseNum + "&Data=" + str;
        WriteBankLogLn2("融汇通POST请求数据:\r\n" + str);
        return str;
    }

    /**
     * webapi 解密融汇返回报文
     *
     * @param is3des
     * @param encryptBase64Data
     * @return
     * @throws Exception
     */
    protected String getSourceData(boolean is3des, String encryptBase64Data) throws Exception {
        String sourceData = encryptBase64Data;
        if (is3des) {
            TripleDESHelper tripleDESHelper = new TripleDESHelper();
            tripleDESHelper.initCBC(getBankConfig().getValueAsString("keyBase64"), getBankConfig().getValueAsString("ivBase64"));
            sourceData = tripleDESHelper.decrypt(encryptBase64Data);
        }
        sourceData = new String(Base64Helper.decode(sourceData), "UTF-8");
        WriteBankLogLn2("解密返回数据：" + sourceData);
        return sourceData;
    }

    /**
     * json 转 map
     *
     * @param jsonStr
     * @return
     */
    public static Map<String, Object> parseJSON2Map(String jsonStr) {
        Map<String, Object> map = new HashMap<String, Object>();
        JSONObject json = JSON.parseObject(jsonStr);
        for (Object k : json.keySet()) {
            Object v = json.get(k);
            if (v instanceof JSONArray) {
                List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
                JSONArray arr = (JSONArray) v;
                for (int i = 0; i < arr.size(); i++) {
                    list.add(parseJSON2Map(arr.getJSONObject(i).toString()));
                }
                map.put(k.toString(), list);
            } else {
                map.put(k.toString(), v);
            }
        }
        return map;
    }

    /**
     * 获取签名字符串
     *
     * @param data
     * @param secretKey
     * @return
     */
    @SuppressWarnings("unchecked")
    protected static String getWebapiToSignDataFromMap(Map<String, Object> data, String secretKey) {
        StringBuilder sb = new StringBuilder();
        Set<String> keySet = data.keySet();
        List<String> keyList = new ArrayList<String>();
        keyList.addAll(keySet);
        // 将所有请求字段名称按ASCII码从小到大排序
        Collections.sort(keyList);
        for (String s : keyList) {
            // Signature字段不参与运算
            if ("Signature".equals(s)) {
                continue;
            }
            Object obj = data.get(s);
            // 空值不参与运算
            if (obj != null) {
                if (obj instanceof String) {
                    if (!"".equals(obj)) {
                        sb.append(obj).append("_");
                    }
                } else if (obj instanceof Map) {
                    getWebapiToSignDataFromMap((Map<String, Object>) obj, secretKey);
                } else if (obj instanceof List) {
                    List<?> listObj = (List<?>) obj;
                    for (Object o : listObj) {
                        getWebapiToSignDataFromMap((Map<String, Object>) o, secretKey);
                    }
                } else {
                    sb.append(String.valueOf(obj)).append("_");
                }
            }
        }
        sb.append(secretKey);
        return sb.toString();
    }

    /**
     * 生产公共请求头
     *
     * @param method 指令名称，见附录接口名称
     * @param method 接口请求参数信息的集合
     * @return
     */
    protected Map<String, String> genCommonReqMap(String enterpriseNum, String method, String bizContent) throws UnsupportedEncodingException {
        HashMap<String, String> reqMap = new HashMap<String, String>();

        reqMap.put("enterpriseNum", enterpriseNum);
        reqMap.put("method", method);
        reqMap.put("format", "JSON");
        reqMap.put("charset", getCharset());
        reqMap.put("signType", "MD5");
        // yyyyMMddHHmmssfff
        String dateFormat = Format.DateTime17SSSFormat.format(new Date());
        reqMap.put("transSeqID", dateFormat);
        reqMap.put("version", "1.0");
        reqMap.put("bizContent", bizContent);
        return reqMap;
    }

    /**
     * 发送融汇
     *
     * @param actItem       指令名称，见附录接口名称
     * @param reqMap        接口请求参数信息的集合
     * @param enterpriseNum 企业号
     * @param url           请求URL
     * @return
     */

    protected String[] sendToFnGrd(ActSetItem actItem, Map<String, String> reqMap, String enterpriseNum, String url) throws Exception {
        // 签名
        String signStr = getSignStr(reqMap, actItem.saltKey);
        String signature = signFnGrd(actItem, signStr);
        reqMap.put("sign", signature);
        // post请求数据
        String reqPostJson = getReqPostJson(reqMap);
        String reqSendData = getReqSendData(enterpriseNum, reqPostJson);
        return sendToFnGrd(reqSendData, url);
    }

    public Map<String, String> jsonToStrMap(String json) {
        Map<String, String> responseMap = new HashMap<String, String>();
        JSONObject jsonObject = JSON.parseObject(json);
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {

            responseMap.put(entry.getKey(), jsonObject.getString(entry.getKey()));

        }
        return responseMap;
    }

    /**
     * 下载对账单到本地并读取压缩包中业务明细文件内容
     *
     * @throws Exception
     */
    protected void smsGet(String p_url, String filePath) throws Exception {
        URL url = null;
        HttpURLConnection httpUrlConnection = null;
        InputStream fis = null;
        FileOutputStream fos = null;

        try {
            if (p_url.contains("https://")) {
                url = new URL(null, p_url, new sun.net.www.protocol.https.Handler());
            } else {
                url = new URL(p_url);
            }
            httpUrlConnection = (HttpURLConnection) url.openConnection();
            httpUrlConnection.setConnectTimeout(5 * 1000);
            httpUrlConnection.setDoInput(true);
            httpUrlConnection.setDoOutput(true);
            httpUrlConnection.setUseCaches(false);
            httpUrlConnection.setRequestMethod("GET");
            httpUrlConnection.setRequestProperty("Charsert", "GBK");
            httpUrlConnection.connect();
            fis = httpUrlConnection.getInputStream();
            byte[] temp = new byte[1024];
            int b;
            fos = new FileOutputStream(new File(filePath));
            while ((b = fis.read(temp)) != -1) {
                fos.write(temp, 0, b);
            }
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            try {
                if (fis != null) fis.close();
                if (fos != null) fos.close();
                if (httpUrlConnection != null) httpUrlConnection.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return;
    }
}
